package com.dm.cloud.images.schedule;

import cn.hutool.core.util.StrUtil;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.model.Container;
import com.dm.cloud.core.exception.CustomException;
import com.dm.cloud.images.common.po.ImagesAppTask;
import com.dm.cloud.images.mode.AppArrangeModel;
import com.dm.cloud.images.mode.CommonProp;
import com.dm.cloud.images.service.*;
import com.dm.cloud.images.util.DockerUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.*;

@Component
@Slf4j
public class AppContainerTask {

    @Value("${dm.images.service-id}")
    private String serviceId;

    @Autowired
    private ImagesAppArrangeService imagesAppArrangeService;

    @Autowired
    private ImagesAppTaskService imagesAppTaskService;

    @Autowired
    private ImagesAppTaskContainerService imagesAppTaskContainerService;

    @Autowired
    private ContainerService containerService;

    @Autowired
    private ImagesEndpointService imagesEndpointService;

    /**
     * 线程池
     */
    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);

    private static ConcurrentMap<String, AppContainerThread> concurrentAppCode = new ConcurrentHashMap();

    private static ConcurrentMap<String, Runnable> concurrentContainers = new ConcurrentHashMap();

    /**
     * 每1秒执行一次应用任务
     */
    @Scheduled(fixedDelay = 1000)
    public void taskRun() {
        List<ImagesAppTask> imagesAppTasks = imagesAppTaskService.findByServiceId(serviceId);
        if (imagesAppTasks != null && !imagesAppTasks.isEmpty()) {
            for (ImagesAppTask imagesAppTask : imagesAppTasks) {
                if (imagesAppTask.getStatus()!=2 && !concurrentAppCode.containsKey(imagesAppTask.getAppCode())) {
                    AppArrangeModel appArrangeModel = imagesAppArrangeService.getArrange(imagesAppTask.getAppCode());
                    AppContainerThread thread = new AppContainerThread(appArrangeModel,imagesAppTask);
                    concurrentAppCode.put(imagesAppTask.getAppCode(), thread); //将线程添加到map
                    thread.start();
                }
            }
        }
    }

    public class AppContainerThread extends Thread {

        private AppArrangeModel appArrangeModel;
        private ImagesAppTask imagesAppTask;
        public AppContainerThread(AppArrangeModel appArrangeModel,ImagesAppTask imagesAppTask) {
            this.appArrangeModel = appArrangeModel;
            this.imagesAppTask = imagesAppTask;
        }

        @Override
        public void run() {
            List<List<AppArrangeModel.AppArrangeItem>> arrangeItems = appArrangeModel.getArranges();
            if (arrangeItems != null && !arrangeItems.isEmpty()) {
                List<List<CompletableFuture>> stepFutres = new ArrayList<>();
                Integer x=1;
                for (List<AppArrangeModel.AppArrangeItem> arrangeItemArr : arrangeItems) {
                    if (arrangeItemArr == null && arrangeItemArr.isEmpty()) {
                        log.info("应用:"+appArrangeModel.getAppCode()+"第"+ x +"步执行完成！");
                        continue;
                    }
                    List<CompletableFuture> futures = new ArrayList<>();
                    int timeOut=0;
                    for (AppArrangeModel.AppArrangeItem arrangeItem : arrangeItemArr) {
                        String containerKey = getContainerKey(arrangeItem);
                        if (!AppContainerTask.concurrentContainers.containsKey(containerKey)) {
                            Runnable t=new Runnable() {
                                @Override
                                public void run() {
                                    try {
                                        runAndListenerContainer(arrangeItem);
                                    }catch (Exception e){
                                        e.printStackTrace();
                                        String taskId = imagesAppTask.getId();
                                        String nodeId= arrangeItem.getNodeId();
                                        String containerId= arrangeItem.getContainerId();
                                        imagesAppTaskContainerService.updateStatus(taskId,nodeId,containerId,2);
                                        imagesAppTaskContainerService.updateResoult(taskId,nodeId,containerId,2,e.getMessage());
                                    }

                                }
                            };
                            futures.add(CompletableFuture.runAsync(t,fixedThreadPool));
                            AppContainerTask.concurrentContainers.put(containerKey,t);
                        }else {
                            continue;
                        }
                        if(arrangeItem.getTimeout()> timeOut){
                            timeOut=arrangeItem.getTimeout();
                        }
                    }
                    if (!futures.isEmpty()) {
                        stepFutres.add(futures);
                        Integer finalX = x;
                        CompletableFuture<Object> anyOfFuture = CompletableFuture.anyOf(
                                futures.toArray(new CompletableFuture[futures.size()])
                        ).whenComplete((m, n) -> {
                            log.info("应用:"+appArrangeModel.getAppCode()+"第"+ finalX +"步执行完成！");
                        });
                        try {
                            anyOfFuture.get(timeOut, TimeUnit.SECONDS); //等待执行完成
                        }catch (Exception e){
                            e.printStackTrace();
                            if(e instanceof TimeoutException){
                                log.error("应用:"+appArrangeModel.getAppCode()+" 第"+finalX+"步操作超时");
                            }else{
                                log.error("应用:"+appArrangeModel.getAppCode()+" 第"+finalX+"步操作执行错误："+e.getMessage());
                            }
//                            break;
                        }
                    }
                    x++;
                }
            }
            //更新状态
            imagesAppTask.setStatus(2);
            imagesAppTaskService.update(imagesAppTask);
            //移除队列
            concurrentAppCode.remove(imagesAppTask.getAppCode());
        }

        /**
         * 获取唯一key
         *
         * @param arrangeItem
         * @return
         */
        private String getContainerKey(AppArrangeModel.AppArrangeItem arrangeItem) {
            return arrangeItem.getNodeId() + "_" + arrangeItem.getContainerId();
        }

        /**
         * 执行并且监听容器
         *
         * @param arrangeItem
         */
        private void runAndListenerContainer(AppArrangeModel.AppArrangeItem arrangeItem) {
//            imagesAppArrangeService
            if(imagesAppTask.getTp()==1){
                //启动任务
                startContainer(arrangeItem,false);

            }else if(imagesAppTask.getTp()==2){
                //关闭任务
                stopContainer(arrangeItem);
            }else if(imagesAppTask.getTp()==3){
                //重启任务
                restartContainer(arrangeItem);
            }
            //队列中移除
            AppContainerTask.concurrentContainers.remove(getContainerKey(arrangeItem));

        }

        private void restartContainer(AppArrangeModel.AppArrangeItem arrangeItem) {
            startContainer(arrangeItem,true);
        }

        /**
         * 关闭服务
         * @param arrangeItem
         */
        private void stopContainer(AppArrangeModel.AppArrangeItem arrangeItem) {
            String taskId = imagesAppTask.getId();
            String nodeId= arrangeItem.getNodeId();
            String containerId= arrangeItem.getContainerId();
            String errStr=null;
            //正在执行
            int x=imagesAppTaskContainerService.updateStatus(taskId,nodeId,containerId,1);
            int result=1;
            try {
                containerService.stop(arrangeItem.getNodeId(),arrangeItem.getContainerId());
            }catch (CustomException e){
                e.printStackTrace();
                if(!e.getMsg().contains("容器已经是停止的")){
                    result=2;
                }
                errStr=e.getMsg();
            }catch (Exception e){
                e.printStackTrace();
                result=2;
                errStr=e.getMessage();
            }finally {
                //完成状态
                imagesAppTaskContainerService.updateStatus(taskId,nodeId,containerId,2);
                imagesAppTaskContainerService.updateResoult(taskId,nodeId,containerId,result,errStr);

            }

        }

        /**
         * 关闭服务
         * @param arrangeItem
         */
        private void startContainer(AppArrangeModel.AppArrangeItem arrangeItem,boolean isRestart){
            String taskId = imagesAppTask.getId();
            String nodeId= arrangeItem.getNodeId();
            String containerId= arrangeItem.getContainerId();
            //正在执行
            int x=imagesAppTaskContainerService.updateStatus(taskId,nodeId,containerId,1);
            try {
                if(isRestart){
                    containerService.restart(arrangeItem.getNodeId(),arrangeItem.getContainerId());
                }else{
                    containerService.start(arrangeItem.getNodeId(),arrangeItem.getContainerId());
                }
            }catch (CustomException e){
                e.printStackTrace();
                int result=1;
                if(!e.getMsg().contains("容器已经是启动的")){
                    result=2;
                }
                //完成状态
                imagesAppTaskContainerService.updateStatus(taskId,nodeId,containerId,2);
                imagesAppTaskContainerService.updateResoult(taskId,nodeId,containerId,result,e.getMsg());
                return;
            }catch (Exception e){
                //完成状态
                imagesAppTaskContainerService.updateStatus(taskId,nodeId,containerId,2);
                imagesAppTaskContainerService.updateResoult(taskId,nodeId,containerId,2,e.getMessage());
            }
            String condition = arrangeItem.getSuccessCondition();
            if(StrUtil.isNotEmpty(condition) && condition.startsWith(CommonProp.APP_CONTAINER_CONDITION_LOG)){
                //监听日志
                //日志正则表达式
                String regexStr = condition.substring(CommonProp.APP_CONTAINER_CONDITION_LOG.length());
                //监听类
                ContainerLogRegexResultCallback callbak = new ContainerLogRegexResultCallback(regexStr);
                DockerClient client=null;
                try {
                    client = imagesEndpointService.getClientByNodeId(arrangeItem.getNodeId());

                    //开始
                    client.logContainerCmd(arrangeItem.getContainerId())
                            .withFollowStream(true)
                            .withStdErr(true)
                            .withStdOut(true)
                            .withTail(1000)
                            .exec(callbak);
                    //超时时间
                    Integer timeOut = arrangeItem.getTimeout();
                    String errStr=null;
                    int containerStatus=0; //1代表成功启动  2代表容器内部异常  3代表超时
                    while(timeOut>=0){
                        try {
                            sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        if(callbak.getLogStatus()==1){
                            containerStatus=1;  //成功
                            break;
                        }

                        if(callbak.getLogStatus()==2){
                            containerStatus=2;//异常
                            errStr=callbak.getErrStr();
                            break;
                        }
                        timeOut--;
                    }
                    if(containerStatus==0){
                        containerStatus=3; //超时
                        errStr="启动超时";
                    }
                    imagesAppTaskContainerService.updateResoult(taskId,nodeId,containerId,containerStatus,errStr);

                }catch (Exception e){
                    e.printStackTrace();
                    imagesAppTaskContainerService.updateResoult(taskId,nodeId,containerId,2,e.getMessage());
                }finally {
                    try {
                        client.close();
                    }catch (Exception e1){}
                    try {
                        callbak.close();
                    }catch (Exception e){}
                }

            }else if(StrUtil.isNotEmpty(condition) && condition.startsWith(CommonProp.APP_CONTAINER_CONDITION_TIME)){
                //定时
                //取时间
                String errStr=null;
                int result=1;
                try {
                    Long time=Long.valueOf(condition.substring(CommonProp.APP_CONTAINER_CONDITION_TIME.length()));
                    Thread.sleep(time*1000); //延时
                }catch (Exception e){
                    e.printStackTrace();
                    errStr=e.getMessage();// 超时报错
                    result=2;
                }

                imagesAppTaskContainerService.updateResoult(taskId,nodeId,containerId,result,errStr);
            }else{
                //其它的直接监听容器是启动的就行
                DockerClient client=null;
                try {
                    client = imagesEndpointService.getClientByNodeId(arrangeItem.getNodeId());
                    //超时时间
                    Integer timeOut = arrangeItem.getTimeout();
                    String errStr=null;
                    int containerStatus=0; //1代表成功启动  2代表容器内部异常  3代表超时
                    while(timeOut>=0){
                        try {
                            sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        try {
                            Container detail = DockerUtil.containerDetail(client, arrangeItem.getContainerId());
                            if(detail.getStatus().toLowerCase(Locale.ROOT).equals("running")){
                                containerStatus=1;
                                break;
                            }
                        }catch (Exception e){
                            e.printStackTrace();
                            containerStatus=2;
                            errStr=e.getMessage();
                            break;
                        }
                    }
                    if(containerStatus==0){
                        containerStatus=3;
                        errStr="启动超时";
                    }
                    imagesAppTaskContainerService.updateResoult(taskId,nodeId,containerId,containerStatus,errStr);
                }catch (Exception e){
                    e.printStackTrace();
                }finally {
                    try {
                        client.close();
                    }catch (Exception e1){}
                }
            }
            //完成状态
            imagesAppTaskContainerService.updateStatus(taskId,nodeId,containerId,2);
        }
    }
}
